package com.ahua.mallchat.common.websocket;

import cn.hutool.core.net.url.UrlBuilder;
import com.ahua.mallchat.common.utils.NettyUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpRequest;
import org.springframework.util.StringUtils;

import java.net.InetSocketAddress;
import java.util.Optional;

/**
 * 获取请求头的处理器
 */
public class MyHeaderCollectHandler extends ChannelInboundHandlerAdapter {

    /**
     * 会首先判断该消息是否为 HttpRequest 类型。
     * 如果是 HttpRequest类型的消息，就会提取其中的 token 参数，
     * (webSocket第一次请求是 HttpRequest类型的请求,用于请求协议升级的)
     * 并将其存储到 Netty 的 Channel 属性中，然后继续传播该消息给下一个 ChannelHandler。
     * 所以，这段代码会在接收到 HTTP 请求消息，并且消息中包含 token 参数时被触发执行。
     *
     * @param ctx
     * @param msg
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        //因为只有在第一次的Http请求中,我们才有机会拿到它带的参数,那么我们必须要在第一次请求的时候
        //就将它的参数保存在chanel中,用于后续的使用
        if (msg instanceof HttpRequest) {
            HttpRequest request = (HttpRequest) msg;
            UrlBuilder urlBuilder = UrlBuilder.ofHttp(request.getUri());
            Optional<String> tokenOptional = Optional.of(urlBuilder).map(UrlBuilder::getQuery).map(querys -> querys.get("token")).map(CharSequence::toString);

            //如果token存在
            if (tokenOptional.isPresent()) {
                NettyUtil.setAttr(ctx.channel(), NettyUtil.TOKEN, tokenOptional.get());
            }

            //握手认证的请求格式是这样的: {{wsurl}}?token=dslkdalskdasl
            //因此我们在这里保存了之后,要把这个 ?token=dslkdalskdasl 去掉
            request.setUri(urlBuilder.getPath().toString());

            //取用户ip
            String ip = request.headers().get("X-Real-IP");
            if (!StringUtils.hasText(ip)) {
                InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
                ip = address.getAddress().getHostAddress();
            }
            //将ip保存到channel附件
            NettyUtil.setAttr(ctx.channel(), NettyUtil.IP, ip);

            //我们只需要用一次这个http的处理器，现在用完了直接接移除就好了
            ctx.pipeline().remove(this);
        }
        //继续传播该消息给下一个 ChannelHandler
        ctx.fireChannelRead(msg);
    }
}
